GtkApplication: track screensaver state
authorMatthias Clasen <mclasen@redhat.com>
Thu, 30 Aug 2018 05:06:51 +0000 (01:06 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Fri, 31 Aug 2018 03:17:37 +0000 (23:17 -0400)
A number of applications want to track the state of the screensaver.
Make this information available as a boolean property. We only listen
for state changes when ::register-session is set to TRUE.

This is implemented for unsandboxed D-Bus access by talking
directly to org.gnome.ScreenSaver or org.freedesktop.ScreenSaver,
and for sandboxed D-Bus by using a (new) portal API.
A Quartz implementation is missing.

gtk/gtkapplication-dbus.c
gtk/gtkapplication.c
gtk/gtkapplicationprivate.h

index 57ed678cba9a51d9c1b971e52e17aa94278a844b..25015eb68cb32ed6656526a280973d4a234d9737 100644 (file)
@@ -36,6 +36,9 @@ G_DEFINE_TYPE (GtkApplicationImplDBus, gtk_application_impl_dbus, GTK_TYPE_APPLI
 #define XFCE_DBUS_OBJECT_PATH       "/org/xfce/SessionManager"
 #define XFCE_DBUS_INTERFACE         "org.xfce.Session.Manager"
 #define XFCE_DBUS_CLIENT_INTERFACE  "org.xfce.Session.Client"
+#define GNOME_SCREENSAVER_DBUS_NAME             "org.gnome.ScreenSaver"
+#define GNOME_SCREENSAVER_DBUS_OBJECT_PATH      "/org/gnome/ScreenSaver"
+#define GNOME_SCREENSAVER_DBUS_INTERFACE        "org.gnome.ScreenSaver"
 
 static void
 unregister_client (GtkApplicationImplDBus *dbus)
@@ -172,6 +175,63 @@ stash_desktop_autostart_id (void)
   g_unsetenv ("DESKTOP_AUTOSTART_ID");
 }
 
+static void
+screensaver_signal_session (GDBusProxy     *proxy,
+                            const char     *sender_name,
+                            const char     *signal_name,
+                            GVariant       *parameters,
+                            GtkApplication *application)
+{
+  gboolean active;
+
+  if (!g_str_equal (signal_name, "ActiveChanged"))
+    return;
+
+  g_variant_get (parameters, "(b)", &active);
+  gtk_application_set_screensaver_active (application, active);
+}
+
+static void
+screensaver_signal_portal (GDBusConnection *connection,
+                           const char       *sender_name,
+                           const char       *object_path,
+                           const char       *interface_name,
+                           const char       *signal_name,
+                           GVariant         *parameters,
+                           gpointer          data)
+{
+  GtkApplication   *application = data;
+  gboolean active;
+  GVariant *state;
+
+  if (!g_str_equal (signal_name, "StateChanged"))
+    return;
+
+  g_variant_get (parameters, "(o@a{sv})", NULL, &state);
+  g_variant_lookup (state, "screensaver-active", "b", &active);
+  gtk_application_set_screensaver_active (application, active);
+}
+
+static void
+create_monitor_cb (GObject      *source,
+                   GAsyncResult *result,
+                   gpointer      data)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (source);
+  GError *error = NULL;
+  GVariant *ret = NULL;
+
+  ret = g_dbus_proxy_call_finish (proxy, result, &error);
+  if (ret == NULL)
+    {
+      g_warning ("Creating a portal monitor failed: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_variant_unref (ret);
+}
+
 static void
 gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
                                    gboolean            register_session)
@@ -240,6 +300,27 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
   if (!register_session)
     goto out;
 
+  dbus->ss_proxy = gtk_application_get_proxy_if_service_present (dbus->session,
+                                                                 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+                                                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                                                 G_DBUS_PROXY_FLAGS_NONE,
+                                                                 GNOME_SCREENSAVER_DBUS_NAME,
+                                                                 GNOME_SCREENSAVER_DBUS_OBJECT_PATH,
+                                                                 GNOME_SCREENSAVER_DBUS_INTERFACE,
+                                                                 &error);
+  if (error)
+    {
+      g_debug ("Failed to get the GNOME screensaver proxy: %s", error->message);
+      g_clear_error (&error);
+      g_clear_object (&dbus->ss_proxy);
+    }
+
+  if (dbus->ss_proxy)
+    {
+      g_signal_connect (dbus->ss_proxy, "g-signal",
+                        G_CALLBACK (screensaver_signal_session), impl->application);
+    }
+
   g_debug ("Registering client '%s' '%s'", dbus->application_id, client_id);
 
   res = g_dbus_proxy_call_sync (dbus->sm_proxy,
@@ -352,8 +433,43 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
         {
           g_debug ("Failed to get an inhibit portal proxy: %s", error->message);
           g_clear_error (&error);
+          goto end;
+        }
+
+      if (register_session)
+        {
+          char *token;
+          GVariantBuilder opt_builder;
+
+          /* Monitor screensaver state */
+
+          dbus->session_id = gtk_get_portal_session_path (dbus->session, &token);
+          dbus->state_changed_handler =
+              g_dbus_connection_signal_subscribe (dbus->session,
+                                                  "org.freedesktop.portal.Desktop",
+                                                  "org.freedesktop.portal.Inhibit",
+                                                  "StateChanged",
+                                                  "/org/freedesktop/portal/desktop",
+                                                  NULL,
+                                                  G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+                                                  screensaver_signal_portal,
+                                                  impl->application,
+                                                  NULL);
+          g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
+          g_variant_builder_add (&opt_builder, "{sv}",
+                                 "session_handle_token", g_variant_new_string (token));
+          g_dbus_proxy_call (dbus->inhibit_proxy,
+                             "CreateMonitor",
+                             g_variant_new ("(sa{sv})", "", &opt_builder),
+                             G_DBUS_CALL_FLAGS_NONE,
+                             G_MAXINT,
+                             NULL,
+                             create_monitor_cb, dbus);
+          g_free (token);
         }
     }
+
+end:;
 }
 
 static void
@@ -710,11 +826,21 @@ gtk_application_impl_dbus_finalize (GObject *object)
 {
   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object;
 
+  g_dbus_connection_call (dbus->session,
+                          "org.freedesktop.portal.Desktop",
+                          dbus->session_id,
+                          "org.freedesktop.portal.Session",
+                          "Close",
+                          NULL, NULL, 0, -1, NULL, NULL, NULL);
+
+  g_free (dbus->session_id);
+  g_dbus_connection_signal_unsubscribe (dbus->session, dbus->state_changed_handler);
   g_clear_object (&dbus->inhibit_proxy);
   g_slist_free_full (dbus->inhibit_handles, inhibit_handle_free);
   g_free (dbus->app_menu_path);
   g_free (dbus->menubar_path);
   g_clear_object (&dbus->sm_proxy);
+  g_clear_object (&dbus->ss_proxy);
 
   G_OBJECT_CLASS (gtk_application_impl_dbus_parent_class)->finalize (object);
 }
index 15378d17e23c22dee2c04ba276b3686a8fcc2446..5d26f070886a945be099aaf1fcf74b958b8ef053 100644 (file)
@@ -137,6 +137,7 @@ static guint gtk_application_signals[LAST_SIGNAL];
 enum {
   PROP_ZERO,
   PROP_REGISTER_SESSION,
+  PROP_SCREENSAVER_ACTIVE,
   PROP_APP_MENU,
   PROP_MENUBAR,
   PROP_ACTIVE_WINDOW,
@@ -157,6 +158,7 @@ struct _GtkApplicationPrivate
   guint            last_window_id;
 
   gboolean         register_session;
+  gboolean         screensaver_active;
   GtkActionMuxer  *muxer;
   GtkBuilder      *menus_builder;
   gchar           *help_overlay_path;
@@ -521,6 +523,10 @@ gtk_application_get_property (GObject    *object,
       g_value_set_boolean (value, application->priv->register_session);
       break;
 
+    case PROP_SCREENSAVER_ACTIVE:
+      g_value_set_boolean (value, application->priv->screensaver_active);
+      break;
+
     case PROP_APP_MENU:
       g_value_set_object (value, gtk_application_get_app_menu (application));
       break;
@@ -652,6 +658,24 @@ gtk_application_class_init (GtkApplicationClass *class)
                           FALSE,
                           G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
 
+  /**
+   * GtkApplication:screensaver-active:
+   *
+   * This property is %TRUE if GTK+ believes that the screensaver is
+   * currently active. GTK+ only tracks session state (including this)
+   * when #GtkApplication::register-session is set to %TRUE.
+   *
+   * Tracking the screensaver state is supported on Linux.
+   *
+   * Since: 3.24
+   */
+  gtk_application_props[PROP_SCREENSAVER_ACTIVE] =
+    g_param_spec_boolean ("screensaver-active",
+                          P_("Screensaver Active"),
+                          P_("Whether the screensaver is active"),
+                          FALSE,
+                          G_PARAM_READABLE|G_PARAM_STATIC_STRINGS);
+
   gtk_application_props[PROP_APP_MENU] =
     g_param_spec_object ("app-menu",
                          P_("Application menu"),
@@ -1460,3 +1484,16 @@ gtk_application_get_menu_by_id (GtkApplication *application,
 
   return G_MENU (object);
 }
+
+void
+gtk_application_set_screensaver_active (GtkApplication *application,
+                                        gboolean        active)
+{
+  GtkApplicationPrivate *priv = gtk_application_get_instance_private (application);
+
+  if (priv->screensaver_active != active)
+    {
+      priv->screensaver_active = active;
+      g_object_notify (G_OBJECT (application), "screensaver-active");
+    }
+}
index 59eff0266e561cb4425ef97ee34599611452c961..cbea1973c6cb499a64e1c85385433235ce73f4bc 100644 (file)
@@ -45,6 +45,9 @@ void                    gtk_application_insert_action_group             (GtkAppl
 
 GtkApplicationAccels *  gtk_application_get_application_accels          (GtkApplication           *application);
 
+void                    gtk_application_set_screensaver_active          (GtkApplication           *application,
+                                                                         gboolean                  active);
+
 #define GTK_TYPE_APPLICATION_IMPL                           (gtk_application_impl_get_type ())
 #define GTK_APPLICATION_IMPL_CLASS(class)                   (G_TYPE_CHECK_CLASS_CAST ((class),                     \
                                                              GTK_TYPE_APPLICATION_IMPL,                            \
@@ -129,10 +132,13 @@ typedef struct
   GDBusProxy      *sm_proxy;
   GDBusProxy      *client_proxy;
   gchar           *client_path;
+  GDBusProxy      *ss_proxy;
 
   /* Portal support */
   GDBusProxy      *inhibit_proxy;
   GSList *inhibit_handles;
+  guint            state_changed_handler;
+  char *           session_id;
 } GtkApplicationImplDBus;
 
 typedef struct